package com.shao;

import com.shao.utils.date.DateUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.LongAdder;

/**
 * 雪花算法Id生成器
 * @author PaoFu
 * @version 1.0
 * @data 2025/4/27 16:11
 */
@Slf4j
public class IdGenerator {

    // 数据中心                                                   5bit 32
    // 机器号                                                     5bit 32
    // 时间戳，选择一个距今比较近的时间作为起始时间，不适用1970-01-01     42bit
    // 序列号                                                     12bit

    // 起始时间戳
    private static final long START_STAMP = DateUtils.getTime("2020-01-01");
    private static final long DATA_CENTER_BIT = 5L;
    private static final long MACHINE_BIT = 5L;
    private static final long SEQUENCE_BIT = 12L;

    // 数据中心最大值
    private static final long DATA_CENTER_MAX = ~(-1L << DATA_CENTER_BIT);
    private static final long MACHINE_MAX = ~(-1L << MACHINE_BIT);
    private static final long SEQUENCE_MAX = ~(-1L << SEQUENCE_BIT);

    // 移动的位数
    private static final long TIMESTAMP_LEFT = DATA_CENTER_BIT + MACHINE_BIT + SEQUENCE_BIT;
    private static final long DATA_CENTER_LEFT = MACHINE_BIT + SEQUENCE_BIT;
    private static final long MACHINE_LEFT = SEQUENCE_BIT;

    private final long dataCenterId;
    private final long machineId;
    // 序列号使用 LongAdder 线程安全对sequence自增
    private final LongAdder longAdder = new LongAdder();
    // 上一次的时间戳（解决时钟回拨的问题）
    private long lastTimestamp;

    public IdGenerator(long machineId, long dataCenterId) {
        if (machineId > MACHINE_MAX || dataCenterId > DATA_CENTER_MAX) {
            throw new IllegalArgumentException("传入的数据中心Id或者机器Id参数不合法");
        }
        this.machineId = machineId;
        this.dataCenterId = dataCenterId;
    }

    public long getId() {
        // 处理时间戳的问题
        long currentTimestamp = System.currentTimeMillis();
        long tempTimestamp = currentTimestamp - START_STAMP;
        // 时钟回拨的问题
        if (tempTimestamp < lastTimestamp) {
            throw new RuntimeException("您的服务器进行了时钟回拨");
        }

        //对相同时间的序列号进行加一
        if (tempTimestamp == lastTimestamp) {
            longAdder.increment();
            if (longAdder.sum() > SEQUENCE_MAX) {
                tempTimestamp = getNextTimestamp(tempTimestamp);
            }
        } else {
            longAdder.reset();
        }
        lastTimestamp = tempTimestamp;

        return tempTimestamp << TIMESTAMP_LEFT | dataCenterId << DATA_CENTER_LEFT | machineId << MACHINE_LEFT | longAdder.sum();
    }

    private long getNextTimestamp(long currentTimestamp) {
        while (lastTimestamp > currentTimestamp) {
            currentTimestamp = System.currentTimeMillis() - START_STAMP;
        }
        return currentTimestamp;
    }
}
